# -*- coding: utf-8 -*-
"""
Created on Sun Apr 13 16:54:10 2025

@author: DAI TIANRU
"""
import pandas as pd
import os
import json
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import chi2_contingency
from matplotlib import rcParams
import gc

# 解决负号显示问题
rcParams['font.sans-serif'] = ['SimHei']
rcParams['axes.unicode_minus'] = False

#指定数据集路径
folder_path1 = 'D:/DTR/MyWork/2025/DataMining/personalProject/10G_data_new'
folder_path2 = 'D:/DTR/MyWork/2025/DataMining/personalProject/30G_data_new'


#解析purchase_history特性中重要的字段
def parse_purchase_history(row):
    try:
        purchase_data = json.loads(row['purchase_history'])
        return pd.Series({
            'average_price': purchase_data['avg_price'],
            'category': purchase_data['categories'],
            'item_count': len(purchase_data['items']),
            'payment_method': purchase_data['payment_method'],
            'payment_status': purchase_data['payment_status'],
            'total_spent':purchase_data['avg_price']*len(purchase_data['items'])
        })
    except (TypeError, json.JSONDecodeError):
        return pd.Series({
            'average_price': None,
            'category': None,
            'item_count': None,
            'payment_method':None,
            'payment_status':None,
            'total_spent':None
        })

#定义方法读取Parquet文件
def batch_read_parquet(folder_path):
    # 检查文件夹是否存在
    if not os.path.exists(folder_path):
        print(f"文件夹 {folder_path} 不存在。")
        return

    # 获取文件夹中所有 .parquet 文件的路径
    parquet_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith('part-00000.parquet')]

    # 遍历每个 .parquet 文件
    all_data = []
    for file_path in parquet_files:
        try:
            #columns_to_read = ['id','age','gender','income','purchase_history']
            #data=pd.read_parquet(file_path,columns=columns_to_read)
            data=pd.read_parquet(file_path)
            # 这里可以对每个数据块进行处理，例如打印数据块的基本信息
            print(f"正在处理文件 {file_path} 的数据块，数据块基本信息：")
            data.info()
            all_data.append(data)
        except Exception as e:
            print(f"处理文件 {file_path} 时出现错误：{e}")
    # 合并所有数据
    combined_df = pd.concat(all_data, ignore_index=True)
    #垃圾回收多余的内存
    del all_data
    gc.collect()
    return combined_df

#读取数据
data=batch_read_parquet(folder_path1)
df = pd.DataFrame(data)



# 查看数据集行数和列数
rows, columns = data.shape
print(rows)
if rows < 10000:
    # 小数据集（行数少于10000）查看全量数据统计信息
    print('数据全部内容的统计信息：')
    print(data.head().to_csv(sep='\t', na_rep='nan'))
else:
    # 大数据集查看数据前几行统计信息
    print('数据前几行内容的统计信息：')
    print(data.head().describe().to_csv(sep='\t', na_rep='nan'))
    

#数据分析可视化
# 1.绘制用户性别分布的饼图
gender_counts = df['gender'].value_counts()
plt.figure(figsize=(6, 6))
plt.pie(gender_counts, labels=gender_counts.index, autopct='%1.1f%%', startangle=140)
plt.axis('equal')  # 保证饼图是圆形
plt.title('Gender Distribution')
plt.show()
    
#2.绘制年龄分布直方图
# 找出年龄区间
max_age = df['age'].max()
min_age = df['age'].min()
print(f"最大年龄是: {max_age}")
print(f"最小年龄是: {min_age}")
#绘制年龄频率分布直方图
plt.figure(figsize=(10, 6))
sns.histplot(df['age'], bins=20, kde=True)
plt.title('Age Distribution')
plt.xlabel('Age')
plt.ylabel('Frequency')
plt.show()

# 定义年龄区间
bins = list(range(18, 101, 10))
labels = [f'{i}-{i + 9}' for i in bins[:-1]]
# 对年龄进行区间划分
df['age_group'] = pd.cut(df['age'], bins=bins, labels=labels, right=False)
# 统计每个区间的人数
age_distribution = df['age_group'].value_counts().sort_index()

print(age_distribution)
plt.figure(figsize=(10, 6))
bars = plt.bar(age_distribution.index.astype(str), age_distribution.values)

# 添加数据标签
for bar in bars:
    height = bar.get_height()
    plt.annotate(f'{height}', xy=(bar.get_x() + bar.get_width() / 2, height),
                 xytext=(0, 3), textcoords='offset points', ha='center', va='bottom')

plt.xlabel('年龄区间')
plt.ylabel('人数')
plt.title('18 - 100 分段年龄区间分布')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()


# 判断是否有重复行
has_duplicates = data.duplicated().any()

if has_duplicates:
    print("DataFrame中存在重复行。")
    # 打印出重复的行
    duplicate_rows = data.duplicated()
    print("重复的行如下：")
    print(duplicate_rows)
else:
    print("DataFrame中不存在重复行。")


#2.数据预处理
#2.1处理缺失值
data = data.dropna()  # 删除包含缺失值的行

#2.2去除 gender 列中不是男或者女的数据，并将性别描述分为'男': 0, '女': 1进行表示
valid_genders = ['男', '女']
data = data[data['gender'].isin(valid_genders)]

# 将 gender 列中的男和女用 0 或 1 表示
gender_mapping = {'男': 0, '女': 1}
data['gender'] = data['gender'].map(gender_mapping)

# 3.处理异常值（年龄不在合理范围内的去除）
q1 = data['age'].quantile(0.25)
q3 = data['age'].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
data = data[(data['age'] >= lower_bound) & (data['age'] <= upper_bound)]

#2.2 读取purchase_history中的关键字段
parsed_data = data.apply(parse_purchase_history, axis=1, result_type='expand')
data['average_price']=parsed_data['average_price']
data['category']=parsed_data['category']
data['item_count']=parsed_data['item_count']
data['payment_method']=parsed_data['payment_method']
data['payment_status']=parsed_data['payment_status']
data['total_spent']=parsed_data['total_spent']
#new_df = pd.DataFrame(data)

del parsed_data
gc.collect()

# 定义支付方式分类映射
payment_mapping = {
    '微信支付': '电子支付',
    '储蓄卡': '银行卡支付',
    '信用卡': '银行卡支付',
    '银联': '银行卡支付',
    '支付宝': '电子支付',
    '云闪付': '电子支付',
    '现金': '现金支付'
}
# 添加分类列
df['payment_method'] = df['payment_method'].map(payment_mapping)

# 重置索引
df = df.reset_index()

# 检查并处理缺失值
print(df.isnull().sum())
df = df.dropna(subset=['age_group', 'payment_method'])


# 数据可视化
#1.卡方校验性别和支付方式之间的关系
# 绘制柱状图
plt.figure(figsize=(10, 6))
sns.countplot(x='gender', hue='payment_method', data=df)
plt.title('Payment Method Distribution by Gender')
plt.xlabel('gender')
plt.ylabel('Count')
plt.legend(title='Payment Method')
plt.show()


# 计算比例
gender_payment_counts = df.groupby(['gender', 'payment_method']).size().unstack()
gender_payment_proportions = gender_payment_counts.div(gender_payment_counts.sum(axis=1), axis=0)
print("每种支付方式在不同性别中的占比：")
print(gender_payment_proportions)

# 进行卡方检验
contingency_table = pd.crosstab(df['gender'], df['payment_method'])
chi2, p, dof, expected = chi2_contingency(contingency_table)
print("\n卡方检验结果：")
print(f"卡方值: {chi2}")
print(f"p 值: {p}")
if p < 0.05:
    print("拒绝零假设，性别和支付方式之间存在显著关联。")
else:
    print("不能拒绝零假设，性别和支付方式之间可能相互独立。")



#2.卡方校验年龄和支付方式之间的关系
# 绘制柱状图
plt.figure(figsize=(10, 6))
sns.countplot(x='age_group', hue='payment_method', data=df)
plt.title('Payment Method Distribution by Age Group')
plt.xlabel('age_group')
plt.ylabel('Count')
plt.legend(title='Payment Method')
plt.show()

# 计算比例
gender_payment_counts = df.groupby(['age_group', 'payment_method']).size().unstack()
gender_payment_proportions = gender_payment_counts.div(gender_payment_counts.sum(axis=1), axis=0)
print("每种支付方式在不同年龄段中的占比：")
print(gender_payment_proportions)

# 进行卡方检验
contingency_table = pd.crosstab(df['age_group'], df['payment_method'])
chi2, p, dof, expected = chi2_contingency(contingency_table)
print("\n卡方检验结果：")
print(f"卡方值: {chi2}")
print(f"p 值: {p}")
if p < 0.05:
    print("拒绝零假设，年龄段和支付方式之间存在显著关联。")
else:
    print("不能拒绝零假设，年龄段和支付方式之间可能相互独立。")
    
#### 3. 数据分布查看
#3.1查看数值型变量的分布情况，如 `age`、`income`、`average_price` 等。
# 提取数值型列
numeric_columns = data.select_dtypes(include=['number']).columns.tolist()

# 绘制箱线图查看分布
for col in numeric_columns:
    plt.figure(figsize=(8, 6))
    sns.boxplot(x=data[col])
    plt.title(f'Boxplot of {col}')
    plt.show()

#### 4. 类别型变量分析
#4.1 查看类别型变量的分布情况，如 `gender`、`country`、`is_active` 等。# 提取类别型列
categorical_columns = data.select_dtypes(include=['object']).columns.tolist()

# 查看类别型变量的分布
for col in categorical_columns:
    value_counts = data[col].value_counts().compute()
    print(f'Distribution of {col}:')
    print(value_counts)
    plt.figure(figsize=(8, 6))
    sns.barplot(x=value_counts.index, y=value_counts.values)
    plt.title(f'Distribution of {col}')
    plt.xticks(rotation=45)
    plt.show()

#### 5. 相关性分析与可视化
#绘制数值型变量之间的相关性热力图。
correlation_matrix = data[numeric_columns].corr()
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.show()